﻿using System.Linq;

using Microsoft.WowAddonStudio.Lua.Model;

using NUnit.Framework;

namespace Microsoft.WowAddonStudio.Lua.Tests.Model
{
    [TestFixture]
    public class DocumentationScopeTest : DocumentationTestBase
    {
        private DocumentationScope globalScope;

        public override void SetUp()
        {
            base.SetUp();

            // Initialize global scope
            globalScope = new DocumentationScope();
        }

        [Test]
        public void GlobalScopeDeclarationsTest()
        {
            // Add global declarations
            globalScope.Documentation.Add(new Table { Name = "Frame1", Prototype = "Frame", Summary = "<Frame name='Frame1' />" });
            globalScope.Documentation.Add(new Variable { Type = LuaType.Number, Name = "x" });

            // Resolve declaration in global scope using exact name
            var frame1 = globalScope.Resolve<Declaration>("Frame1");
            var x = globalScope.Resolve<Declaration>("x");

            Assert.IsNotNull(frame1);
            Assert.IsTrue(frame1 is Table);

            Assert.IsNotNull(x);
            Assert.IsTrue(x is Variable);
        }

        [Test]
        public void LocalScopeResolveExactNameTest()
        {
            // Initialize local scope
            var localScope = new DocumentationScope(globalScope);

            // Add global declaration
            globalScope.Documentation.Add(new Variable { Type = LuaType.Number, Name = "x" });

            // Add local declaration
            localScope.Documentation.Add(new Variable { Type = LuaType.String, Name = "x" });

            // Resolve the variable using the local scope
            var x = localScope.Resolve<Declaration>("x");

            // Assert that the declaration is coming from the local scope
            Assert.AreEqual(LuaType.String, x.Type);
            Assert.AreSame(localScope.Documentation.Resolve<Declaration>("x"), x);
        }

        [Test]
        public void LocalScopeDeclarationsTest()
        {
            // Initialize local scope
            var localScope = new DocumentationScope(globalScope);

            // Add global declaration
            globalScope.Documentation.Add(new Variable { Type = LuaType.Number, Name = "x" });

            // Add local declaration
            localScope.Documentation.Add(new Variable { Type = LuaType.Number, Name = "y" });

            // Assert the declarations valid in the local scope
            Assert.AreEqual(2, localScope.Declarations.Count());
        }

        [Test]
        public void MergeScopeTest()
        {
            var firstGlobalScope = new DocumentationScope();
            var secondGlobalScope = new DocumentationScope();

            // Add declarations to global scopes
            firstGlobalScope.Documentation.Add(new Variable { Type = LuaType.Number, Name = "x" });
            secondGlobalScope.Documentation.Add(new Variable { Type = LuaType.Number, Name = "y" });

            globalScope.Merge(firstGlobalScope);
            globalScope.Merge(secondGlobalScope);

            Assert.AreEqual(2, globalScope.Declarations.Count());
            Assert.IsNotNull(globalScope.Resolve<Declaration>("x"));
            Assert.IsNotNull(globalScope.Resolve<Declaration>("y"));
        }

        [Test]
        public void MergeScopePrototypeTest()
        {
            var widgetApiDocumentation = this.DeserializeDocumentation(widgetApiRelativePath);

            // Create a global scope for the FrameXML Widget API and our own global scope
            var frameXmlGlobalScope = new DocumentationScope(widgetApiDocumentation);
            var userGlobalScope = new DocumentationScope();

            // Add a frame declaration to the user global scope
            userGlobalScope.Documentation.Add(new Table
                                              {
                                                  Name = "Frame1",
                                                  Prototype = "Frame"
                                              });

            // Merge both global scopes into a single global scope
            globalScope.Merge(frameXmlGlobalScope);
            globalScope.Merge(userGlobalScope);

            // Assert that Frame1 can be resolved in the global scope, its prototype was merged
            // and has declarations
            var frame1 = globalScope.Resolve<Table>("Frame1");

            Assert.IsNotNull(frame1);
            Assert.IsTrue(frame1.IsPrototypeMerged);
            Assert.AreNotEqual(0, frame1.Declarations.Count());
        }
    }
}